/**
 * @(#)CodeEntityRelationAnalyzer.java, 2008-4-30.
 * Copyright 2008  Program Analysis Research Group(PARG),Institute of Software, Peking University.
 * All rights reserved.
 */
package edu.pku.sei.pgie.analyzer.internal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;

import edu.pku.sei.pgie.Activator;
import edu.pku.sei.pgie.analyzer.core.Context;
import edu.pku.sei.pgie.analyzer.core.ICodeEntityRelation;
import edu.pku.sei.pgie.analyzer.core.ICodeEntityRelationFilter;
import edu.pku.sei.pgie.analyzer.core.ICollector;
import edu.pku.sei.pgie.analyzer.core.SourceTreeVisitor;
import edu.pku.sei.pgie.beans.Link;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author HeLi
 */
public class CodeEntityRelationAnalyzer extends SourceTreeVisitor {
    private static Log logger = LogFactory
            .getLog(CodeEntityRelationAnalyzer.class);

    List<ICodeEntityRelation> relationList;

    ASTParser parser = ASTParser.newParser(AST.JLS3);

    CodeEntitySymbolTable symbolTable;

    List<ICodeEntityRelationFilter> relationFilters;

    ICollector collector;

    public CodeEntityRelationAnalyzer(CodeEntitySymbolTable symbolTable,
            Context context) throws Exception {
        super();
        relationList = new ArrayList<ICodeEntityRelation>();
        relationFilters = new ArrayList<ICodeEntityRelationFilter>();
        collector = context.getCollector();
        this.symbolTable = symbolTable;
        init();
    }

    public void registerFilter(ICodeEntityRelationFilter filter) {
        if (filter != null)
            relationFilters.add(filter);
    }

    public void removeFilter(ICodeEntityRelationFilter filter) {
        if (filter != null) {
            relationFilters.remove(filter);
        }
    }

    protected void init() {
        relationList.addAll(Activator.getDefault().getCodeEntityRelations());
    }

    public boolean visit(ICompilationUnit compilationUnit) {
        if (compilationUnit == null)
            return false;
        parser.setSource(compilationUnit);
        parser.setResolveBindings(true);
        CompilationUnit root = (CompilationUnit) parser.createAST(null);

        if (symbolTable == null)
            return false;

        Iterator relationIterator = relationList.iterator();

        while (relationIterator.hasNext()) {
            ICodeEntityRelation relation = (ICodeEntityRelation) relationIterator
                    .next();
            // logger.debug("running relationship
            // analysis[type="+relation.getRelationName()+"]");
            String[][] links = null;
            try {
                links = relation.collectCallBack(root, symbolTable);
                if (links != null) {
                    for (int i = 0; i < links.length; i++) {
                        String[] row = links[i];
                        if (isSatisfied(row))
                            continue;
                        Long codeId1 = symbolTable.getCodeID(row[0]);
                        Long codeId2 = symbolTable.getCodeID(row[1]);
                        if (codeId1 == null || codeId2 == null)
                            continue;
                        Link link = new Link();
                        link.setStartPoint(codeId1.longValue());
                        link.setEndPoint(codeId2.longValue());
                        link.setType(relation.getRelationName());
                        if (collector != null) {
                            collector.collect(link);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                logger.debug(e);
                continue;
            }
        }
        return false;
    }

    public boolean isSatisfied(String[] link) {
        Iterator iterator = relationFilters.iterator();
        while (iterator.hasNext()) {
            ICodeEntityRelationFilter filter = (ICodeEntityRelationFilter) iterator
                    .next();
            if (filter.isSatisfied(link[0], link[1]))
                return true;
        }
        return false;
    }
}
